package proxy;

import Config.RpcConfig;
import Constant.RpcConstant;
import Constant.SystemConstant;
import boot.RpcApplication;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.sun.source.tree.StatementTree;
import faultTolerant.FaultTolerant;
import faultTolerant.FaultTolerantFactory;
import loadBalancer.LoadBalance;
import loadBalancer.LoadBalancerFactory;
import lombok.extern.slf4j.Slf4j;
import model.RpcRequest;
import model.RpcResponse;
import model.ServiceInfo;
import register.Registry;
import register.RegistryFactory;
import retry.RetryStrategy;
import retry.RetryStrategyFactory;
import serializer.JdkSerializer;
import serializer.Serializer;
import serializer.SerializerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 服务代理逻辑
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1.动态获取序列化器（配置文件）
        Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializerCategory());
        log.info(SystemConstant.SERIALIZER_NAME + RpcApplication.getRpcConfig().getSerializerCategory());
        System.out.println(SystemConstant.SERVICE_NAME + method.getDeclaringClass().getName());
        System.out.println(SystemConstant.METHOD_NAME + method.getName());

        // 2.构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .avgs(args)
                .parameterType(method.getParameterTypes())
                .build();

        // 3.发请求
        try {
            byte[] bytes = serializer.serialize(rpcRequest);
            // 3.1动态获取调用地址，拿到注册中心对象
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());

            // 3.2构造要访问的服务元信息（有服务名称和版本即可）
            String serviceName = method.getDeclaringClass().getName();
            ServiceInfo serviceInfo = new ServiceInfo();
            serviceInfo.setServiceName(serviceName);
            serviceInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);

            // 3.3查询出来的服务列表
            List<ServiceInfo> serviceInfoList = registry.serviceDiscovery(serviceInfo.getServiceKey());
            Map<String, Object> requestParams = new HashMap<>();

            // 3.4构造请求参数
            requestParams.put(SystemConstant.CONSISTENT_HASH_REPLICA_NUM, rpcRequest.getMethodName());

            // 3.5负载均衡,通过负载均衡工厂拿到指定的负载均衡器
            LoadBalance selectedLoadBalancer = LoadBalancerFactory
                    .getInstance(RpcApplication.getRpcConfig().getBalancer());
            log.info(SystemConstant.LOAD_BALANCER_NAME + RpcApplication.getRpcConfig().getBalancer());

            // 3.6调用负载均衡器的选择算法
            ServiceInfo selectedServiceInfo = selectedLoadBalancer.selectServiceFromList(requestParams,
                    serviceInfoList);

            // 3.7拿到重试对象,采取了重试机制
            RetryStrategy retryStrategy = RetryStrategyFactory
                    .getInstance(RpcApplication.getRpcConfig().getRetryStrategy());
            log.info(SystemConstant.RETRY_STRATEGY_NAME + RpcApplication.getRpcConfig().getRetryStrategy());
            RpcResponse rpcResponse = retryStrategy.retry(() -> sendRequest(selectedServiceInfo, bytes, serializer));
            if (!Objects.isNull(rpcResponse)) {
                return rpcResponse.getData();
            }
        } catch (Exception e) {
            // 3.8重试达到最大次数，使用容错机制来提高框架健壮性
            FaultTolerant faultTolerant = FaultTolerantFactory
                    .getInstance(RpcApplication.getRpcConfig().getFaultTolerantKey());
            log.error(SystemConstant.FAULT_TOLERANT_NAME + RpcApplication.getRpcConfig().getFaultTolerantKey());
            faultTolerant.tolerant(null, e);
        }
        return null;
    }

    /**
     * 发请求并处理返回数据，遇到异常抛出
     * 
     * @param serviceInfo
     * @param bytes
     * @param serializer
     * @return
     * @throws Exception
     */
    private RpcResponse sendRequest(ServiceInfo serviceInfo, byte[] bytes, Serializer serializer) throws Exception {
        HttpResponse httpResponse = HttpRequest.post(serviceInfo.getAddress()).body(bytes).execute();
        byte[] resonseBytes = httpResponse.bodyBytes();
        RpcResponse rpcResponse = serializer.deserialize(resonseBytes, RpcResponse.class);
        return rpcResponse;
    }
}
